﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OpenTK;
using OpenTK.Graphics;

namespace LasRendering
{
    public class RoofEdge
    {
        private List<Vector3> positionList = new List<Vector3>();
        private Vector3 direction;
        private Vector3 centroid;
        private float thicknessSquared;
        private int nextDirCompCount;
        private bool dirty;
        private Vector3 endpoint1;
        private Vector3 endpoint2;

        public Color4 color;

        public RoofEdge(Vector3 position, Vector3 direction, float thickness, int numVerticesWhenComputingTheDirection)
        {
            this.positionList.Add(position);
            this.direction = Vector3.Normalize(direction);
            this.centroid = position;
            this.thicknessSquared = thickness * thickness;
            this.nextDirCompCount = numVerticesWhenComputingTheDirection;
            this.dirty = true;
        }

        public float DirectionFromPointSquared(Vector3 point)
        {
            Vector3 v = point - this.centroid;
            float d = Vector3.Dot(v, this.direction);
            return v.LengthSquared - d * d;
        }

        public bool IsOnRoofEdge(Vector3 target)
        {
            return this.DirectionFromPointSquared(target) < this.thicknessSquared;
        }

        public void Add(Vector3 position)
        {
            this.positionList.Add(position);
            if (positionList.Count > this.nextDirCompCount)
            {
                ComputeDirectionAndCentroid();
            }
            this.dirty = true;
        }

        public void GetEndpoints(out Vector3 srcEndpoint, out Vector3 destEndpoint)
        {
            if (this.dirty)
            {
                ComputeDirectionAndCentroid();

                srcEndpoint = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue);
                destEndpoint = new Vector3(float.MinValue, float.MinValue, float.MinValue);
                if (Math.Abs(this.direction.X) > Math.Abs(this.direction.Y))
                {
                    foreach (var position in this.positionList)
                    {
                        if (position.X < srcEndpoint.X)
                        {
                            srcEndpoint = position;
                        }
                        if (position.X > destEndpoint.X)
                        {
                            destEndpoint = position;
                        }
                    }

                    if (this.direction.X < 0)
                    {
                        var t = srcEndpoint;
                        srcEndpoint = destEndpoint;
                        destEndpoint = t;
                    }
                }
                else
                {
                    foreach (var position in this.positionList)
                    {
                        if (position.Y < srcEndpoint.Y)
                        {
                            srcEndpoint = position;
                        }
                        if (position.Y > destEndpoint.Y)
                        {
                            destEndpoint = position;
                        }
                    }

                    if (this.direction.Y < 0)
                    {
                        var t = srcEndpoint;
                        srcEndpoint = destEndpoint;
                        destEndpoint = t;
                    }
                }

                this.endpoint1 = srcEndpoint;
                this.endpoint2 = destEndpoint;
                this.dirty = false;
            }
            else
            {
                srcEndpoint = this.endpoint1;
                destEndpoint = this.endpoint2;
            }
        }

        public void Merge(RoofEdge edge)
        {
            this.positionList.AddRange(edge.positionList);
            ComputeDirectionAndCentroid();
            this.dirty = true;
        }

        public bool Contains(Vector3 position)
        {
            return this.positionList.Contains(position);
        }

        public Vector3 Direction
        {
            get
            {
                return this.direction;
            }
        }

        public Vector3 Centroid
        {
            get
            {
                return this.centroid;
            }
        }

        public int Count
        {
            get
            {
                return this.positionList.Count;
            }
        }

        public List<Vector3> Positions
        {
            get
            {
                return this.positionList;
            }
        }

        private void ComputeDirectionAndCentroid()
        {
            this.direction = CloudPointsHelper.ComputeEigenvectorOfMaxEigenvalue(this.positionList);
            this.direction = Vector3.Normalize(this.direction);
            Vector3 sum = Vector3.Zero;
            for (int i = 0; i < this.positionList.Count; i++)
            {
                sum += this.positionList[i];
            }
            this.centroid = sum / this.positionList.Count;
            this.nextDirCompCount = this.positionList.Count * 2;
        }
    }
}
